App.require(function () {
    function Program(gl, vs, fs) {
        this.gl = gl;
        this.vertexShader = Program.createShader(gl, gl.VERTEX_SHADER, vs);
        this.fragmentShader = Program.createShader(gl, gl.FRAGMENT_SHADER, fs);
        this.program = Program.createProgram(gl, this.vertexShader, this.fragmentShader);
        this.uniform = Program.createUniforms(gl, this.program);
        this.attribute = Program.createAttributes(gl, this.program);
    }
    
    Program.prototype = {
        constructor: Program,
        use: function () {
            this.gl.useProgram(this.program);
            return this;
        },
        dispose: function () {
            var gl = this.gl;
            var program = this.program;
            for (var key in program.attribute) {
                gl.disableVertexAttribArray(program.attribute[key]);
            }
            gl.deleteShader(this.vertexShader);
            gl.deleteShader(this.fragmentShader);
            gl.deleteProgram(program);
        },
    };
    
    Program.createShader = function (gl, type, source) {
        var shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
    
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            console.log(gl.getShaderInfoLog(shader));
            return null;
        };
    
        return shader;
    };
    
    Program.createProgram = function (gl, vshader, fshader) {
        var program = gl.createProgram();
        gl.attachShader(program, vshader);
        gl.attachShader(program, fshader);
        gl.linkProgram(program);
    
        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            console.log("Could not initialise shaders");
            return null;
        }
    
        return program;
    };
    
    Program.createUniforms = function (gl, program) {
        var uniform = {};
    
        var activeUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
        for (var i = 0; i < activeUniforms; i++) {
            var u = gl.getActiveUniform(program, i);
            var name = u.name;
            var position = gl.getUniformLocation(program, name);
            uniform[name] = position;
        }
    
        return uniform;
    };
    
    Program.createAttributes = function (gl, program) {
        var attribute = {};
    
        var activeAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
        for (var i = 0; i < activeAttributes; i++) {
            var a = gl.getActiveAttrib(program, i);
            attribute[a.name] = i;
        }
    
        return attribute;
    };

    App.define("program", {
        Program: Program
    })
})